using FileImportSystem2.Db.Entities;
using MiniExcelLibs;
using SQLite;
using System;
using System.Collections.Generic;
using System.Data;
using System.IO;
using System.Linq;
using System.Threading.Tasks;

namespace FileImportSystem2.Services
{
    /// <summary>
    /// 文件导入服务类
    /// 负责Excel文件数据的导入、解析和存储到SQLite数据库
    /// </summary>
    internal class FileImporterService
    {
        /// <summary>
        /// 数据库文件路径
        /// </summary>
        public readonly string _dbPath;
        /// <summary>
        /// 数据库类型（匹配/数据）
        /// </summary>
        private readonly string _dbType;
        /// <summary>
        /// 数据库文件所在目录
        /// </summary>
        private readonly string _dbFd;
        /// <summary>
        /// 要导入的文件路径
        /// </summary>
        private readonly string _filePath;
        /// <summary>
        /// SQLite数据库连接对象
        /// </summary>
        private readonly SQLiteConnection _db;
        /// <summary>
        /// 完整日期格式 (YYYY-MM-DD)
        /// </summary>
        private readonly string _dateFull = "0000-00-00";
        /// <summary>
        /// 月份日期格式 (YYYY-MM)
        /// </summary>
        private readonly string _dateMonth = "0000-00";
        /// <summary>
        /// 批量插入数据的批次大小
        /// </summary>
        private readonly int _batchSize = 10000;
        /// <summary>
        /// 用于确保数据库操作线程安全的锁对象
        /// </summary>
        static readonly object _lockObj = new object();

        #region 数据库初始化
        /// <summary>
        /// 根据文件路径，初始化导入服务
        /// </summary>
        /// <param name="filePath">导入文件路径</param>
        public FileImporterService(string filePath)
        {
            // 设置需要导入的文件路径
            _filePath = filePath;
            // 获取文件名用于确定数据库类型
            string fName = Path.GetFileNameWithoutExtension(filePath);
            
            // 根据文件名判断数据库类型并设置相应的数据库路径
            if (fName.IndexOf("匹配") >= 0)
            {
                _dbType = "匹配";
                // 匹配文件导入到主数据库
                _dbPath = AppConfig.dbMainPath;
                _dbFd = Path.GetDirectoryName(_dbPath) ?? string.Empty;
            }
            else
            {
                _dbType = "数据";
                _dbFd = AppConfig.dbMonFd;
                // 从文件路径提取日期信息
                (_dateFull, _dateMonth) = FileNameServer.getDateFromFilePath(filePath);
                // 构建月度数据库文件路径
                _dbPath = Path.Combine(_dbFd, _dateMonth + ".db");
            }
            
            // 确保数据库目录存在
            if (!Directory.Exists(_dbFd))
            {
                Directory.CreateDirectory(_dbFd);
            }
            
            // 初始化SQLite数据库连接
            _db = new SQLiteConnection(_dbPath);

            // 根据数据库类型创建相应的数据表
            switch (_dbType)
            {
                case "匹配":
                    // 创建匹配数据表
                    _db.CreateTable<SenderMatch>();
                    _db.CreateTable<ShenzhouMatch>();
                    break;
                case "数据":
                    // 可选：启用WAL模式提高性能
                    //_db.ExecuteScalar<string>("PRAGMA journal_mode=WAL;");
                    //_db.Execute("PRAGMA journal_mode=WAL;");
                    
                    // 创建所有需要的数据表
                    _db.CreateTable<SenderData>();
                    _db.CreateTable<ShenzhouData>();
                    _db.CreateTable<OutboundInvoiceData>();
                    _db.CreateTable<CenterTransferFeeData>();
                    _db.CreateTable<UnMatchedData>();
                    break;
                default:
                    throw new Exception("未知的数据库类型");
            }
        }

        #endregion

        #region 文件类型映射以及文件导入
        /// <summary>
        /// 异步运行文件导入任务
        /// </summary>
        /// <param name="filePath">文件路径</param>
        /// <returns>包含导入结果、消息和导入数量的任务</returns>
        public Task<(bool res, string mark, int ImpNum)> RunTask(string filePath)
        {
            return Task.Run(() => Import(filePath));
        }

        /// <summary>
        /// 导入文件: 使用初始化时设置的文件路径
        /// </summary>
        /// <returns>导入结果(成功/失败)、消息和导入记录数量</returns>
        public (bool res, string mark, int ImpNum) Import()
        {
            string filePath = _filePath;
            return Import(filePath);
        }

        /// <summary>
        /// 导入文件
        /// </summary>
        /// <param name="filePath">文件路径</param>
        /// <returns>导入结果(成功/失败)、消息和导入记录数量</returns>
        public (bool res, string mark, int ImpNum) Import(string filePath)
        {
            try
            {
                var fileName = Path.GetFileName(filePath);
                // 确定文件类型
                var fileType = getTypeFromFilePath(fileName);
                
                // 提取文件中的日期信息
                (string dateFull, string dateMonth) = FileNameServer.getDateFromFilePath(filePath);
                
                // 验证文件日期与数据库日期是否一致
                if (dateMonth != _dateMonth)
                {
                    return (false, $"文件名中日期信息与数据库初始化日期不符: {fileName}：{dateMonth}/{_dateMonth}", 0);
                }
                
                // 根据文件类型调用不同的导入方法
                switch (fileType)
                {
                    case "匹配":
                        return ImportMatchData(filePath);
                    case "发件":
                        return ImportSenderData(filePath, dateFull);
                    case "神州":
                        return ImportShenzhouData(filePath, dateFull);
                    case "出港账单":
                        return ImportOutboundData(filePath, dateFull);
                    case "中转费":
                        return ImportCenterTransferData(filePath, dateFull);
                    case "UnMatched":
                        return ImportUnMatchData(filePath);
                    default:
                        return (false, $"未知的文件类型: {fileName}", 0);
                }
            }
            catch (Exception ex)
            {
                return (false, ex.Message, 0);
            }
        }

        /// <summary>
        /// 文件名与表名映射规则
        /// 用于根据文件名确定数据类型
        /// </summary>
        private static readonly Dictionary<string, string>
            _tableMapping = new(StringComparer.OrdinalIgnoreCase)
        {
            {"UnMatched","UnMatched"}
            ,{ "匹配","匹配"}
            ,{"发件","发件" }
            ,{"神州","神州" }
            ,{"出港账单","出港账单"}
            ,{"中转费","中转费"}
        };

        /// <summary>
        /// 根据文件路径，确定文件类型
        /// </summary>
        /// <param name="filePath">文件路径</param>
        /// <returns>文件类型，如"匹配"、"发件"等，未匹配则返回"NA"</returns>
        private static string getTypeFromFilePath(string filePath)
        {
            // 优先级1：文件名（不含扩展名）完全匹配
            string fileName = Path.GetFileNameWithoutExtension(filePath);

            // 检查是否直接匹配
            if (_tableMapping.TryGetValue(fileName, out var mapping))
                return mapping;

            // 优先级2：文件名包含关键字
            foreach (var key in _tableMapping.Keys)
            {
                if (fileName.Contains(key, StringComparison.CurrentCulture))
                    return _tableMapping[key];
            }

            return "NA";  // 未匹配到规则
        }
        #endregion

        /// <summary>
        /// 导入匹配数据
        /// </summary>
        /// <param name="filePath">文件路径</param>
        /// <returns>导入结果、消息和导入记录数量</returns>
        private (bool res, string mark, int ImpNum) ImportMatchData(string filePath)
        {
            // 确保数据表存在
            _db.CreateTable<SenderMatch>();
            _db.CreateTable<ShenzhouMatch>();
            
            // 读取文件内容，分别获取"发件"和"神州"两个工作表的数据
            var rowsSender = MiniExcel.Query(filePath, useHeaderRow: true, sheetName: "发件");
            var rowsShenzhou = MiniExcel.Query(filePath, useHeaderRow: true, sheetName: "神州");
            
            int numDb = 0;
            
            // 处理发件匹配数据
            var listSender = new List<SenderMatch>();
            foreach (var row in rowsSender)
            {
                var item = new SenderMatch
                {
                    扫描人 = row.扫描人,
                    分部 = row.分部
                };
                
                // 过滤有效数据
                if (item.扫描人 != null && item.分部.Length > 0) 
                {
                    listSender.Add(item); 
                    numDb++;
                }
            }
            
            // 线程安全地批量插入数据
            lock (_lockObj) 
            {
                _db.InsertAll(listSender, "OR Replace", true); 
            }

            // 处理神州匹配数据
            var listShenzhou = new List<ShenzhouMatch>();
            foreach (var row in rowsShenzhou)
            {
                var itemShenzhou = new ShenzhouMatch
                {
                    结算对象 = row.结算对象,
                    分部 = row.分部
                };
                
                // 过滤有效数据
                if (itemShenzhou.结算对象 != null && itemShenzhou.分部.Length > 0) 
                {
                    listShenzhou.Add(itemShenzhou); 
                    numDb++;
                }
            }
            
            // 线程安全地批量插入数据
            lock (_lockObj) 
            {
                _db.InsertAll(listShenzhou, "OR Replace", true); 
            }

            return (true, string.Empty, numDb);
        }


        /// <summary>
        /// 导入未匹配数据
        /// </summary>
        /// <param name="filePath">文件路径</param>
        /// <returns>导入结果、消息和导入记录数量</returns>
        private (bool res, string mark, int ImpNum) ImportUnMatchData(string filePath)
        {
            // 确保数据表存在
            _db.CreateTable<UnMatchedData>();
            // 读取文件内容
            var rows = MiniExcel.Query(filePath, useHeaderRow: true);

            // 用于存储待插入的数据
            var ListDb = new List<UnMatchedData>();
            int numDb = 0;
            
            // 遍历文件中的每一行数据
            foreach (IDictionary<string, object> row in rows.Select(v => (IDictionary<string, object>)v))
            {
                string 单号;
                string 结算对象;
                string 分部;
                string 发件仓;
                
                // 提取字段值，处理可能的空值
                if (!row.TryGetValue("单号", out var obj单号)) { continue; } else { 单号 = (string)obj单号; }
                if (!row.TryGetValue("结算对象", out var obj结算对象)) { 结算对象 = string.Empty; } else { 结算对象 = (string)obj结算对象; }
                if (!row.TryGetValue("分部", out var obj分部)) { 分部 = string.Empty; } else { 分部 = (string)obj分部; }
                if (!row.TryGetValue("发件仓", out var obj发件仓)) { 发件仓 = string.Empty; } else { 发件仓 = (string)obj发件仓; }

                // 检查数据库中是否已存在该单号的数据
                var existingItem = _db.Table<UnMatchedData>()
                                         .FirstOrDefault(x => x.单号 == (string)单号);

                // 如果存在现有数据，使用现有数据替换空值，实现部分更新
                if (existingItem != null)
                {
                    if (string.IsNullOrEmpty(结算对象)) { 结算对象 = existingItem.结算对象; }
                    if (string.IsNullOrEmpty(分部)) { 分部 = existingItem.分部; }
                    if (string.IsNullOrEmpty(发件仓)) { 发件仓 = existingItem.发件仓; }
                }

                // 如果所有关键字段都为空，则跳过该记录
                if (string.IsNullOrEmpty(结算对象) && string.IsNullOrEmpty(分部) && string.IsNullOrEmpty(发件仓))
                {
                    continue;
                }

                // 创建数据实体
                var item = new UnMatchedData
                {
                    单号 = 单号,
                    结算对象 = 结算对象,
                    分部 = 分部,
                    发件仓 = 发件仓,
                };
                
                // 添加到待插入列表
                if (item.单号 != null) 
                {
                    ListDb.Add(item); 
                    numDb++;
                }
                
                // 当达到批次大小时，执行批量插入
                if (ListDb.Count >= _batchSize)
                {
                    lock (_lockObj) 
                    {
                        _db.InsertAll(ListDb, "OR Replace", true); 
                    }
                    ListDb.Clear();
                }
            }

            // 插入剩余的数据
            if (ListDb.Count > 0) 
            {
                lock (_lockObj) 
                {
                    _db.InsertAll(ListDb, "OR Replace", true); 
                }
            }

            return (true, string.Empty, numDb);
        }


        /// <summary>
        /// 导入发件数据
        /// </summary>
        /// <param name="filePath">文件路径</param>
        /// <param name="dateFull">完整日期字符串</param>
        /// <returns>导入结果、消息和导入记录数量</returns>
        private (bool res, string mark, int ImpNum) ImportSenderData(string filePath, string dateFull)
        {
            // 确保数据表存在
            _db.CreateTable<SenderData>();
            // 读取文件内容
            var rows = MiniExcel.Query(filePath, useHeaderRow: true);
            
            // 用于存储待插入的数据
            var ListDb = new List<SenderData>();
            int numDb = 0;
            
            // 遍历文件中的每一行数据
            foreach (var row in rows)
            {
                // 创建数据实体并填充字段
                var item = new SenderData
                {
                    Date = dateFull,
                    单号 = row.单号,
                    扫描人 = row.扫描人,
                    扫描人编码 = row.扫描人编码,
                    扫描网点 = row.扫描网点,
                    发件仓 = string.Empty,  // 初始化时为空，后续可能通过匹配填充
                    ImportTime = DateTime.Now,
                    IsMatched = 0  // 标记为未匹配状态
                };
                
                // 添加有效数据到列表
                if (item.单号 != null) 
                {
                    ListDb.Add(item); 
                    numDb++;
                }
                
                // 批量插入数据，避免频繁数据库操作
                if (ListDb.Count >= _batchSize)
                {
                    lock (_lockObj) 
                    {
                        _db.InsertAll(ListDb, "OR Replace", true); 
                    }
                    ListDb.Clear();
                }
            }

            // 插入剩余的数据
            if (ListDb.Count > 0) 
            {
                lock (_lockObj) 
                {
                    _db.InsertAll(ListDb, "OR Replace", true); 
                }
            }

            return (true, string.Empty, numDb);
        }


        /// <summary>
        /// 导入神州数据
        /// </summary>
        /// <param name="filePath">文件路径</param>
        /// <param name="dateFull">完整日期字符串</param>
        /// <returns>导入结果、消息和导入记录数量</returns>
        private (bool res, string mark, int ImpNum) ImportShenzhouData(string filePath, string dateFull)
        {
            // 确保数据表存在
            _db.CreateTable<ShenzhouData>();
            // 读取文件内容，从A2单元格开始读取（跳过标题行）
            var rows = MiniExcel.Query(filePath, useHeaderRow: true, startCell: "A2");
            
            // 用于存储待插入的数据
            var ListDb = new List<ShenzhouData>();
            int numDb = 0;
            
            // 遍历文件中的每一行数据
            foreach (var row in rows)
            {
                // 创建数据实体并填充字段
                var item = new ShenzhouData
                {
                    Date = dateFull,
                    单号 = row.运单号,
                    结算对象 = row.结算对象,
                    揽收业务员 = row.揽收业务员,
                    分部 = string.Empty,  // 初始化时为空，后续可能通过匹配填充
                    发件仓 = string.Empty,  // 初始化时为空，后续可能通过匹配填充
                    ImportTime = DateTime.Now,
                    Is分部Matched = 0,  // 标记分部未匹配
                    Is发件仓Matched = 0  // 标记发件仓未匹配
                };

                // 添加有效数据到列表
                if (item.单号 != null) 
                {
                    ListDb.Add(item); 
                    numDb++;
                }
                
                // 批量插入数据
                if (ListDb.Count >= _batchSize)
                {
                    lock (_lockObj) 
                    {
                        _db.InsertAll(ListDb, "OR Replace", true); 
                    }
                    ListDb.Clear();
                }
            }

            // 插入剩余的数据
            if (ListDb.Count > 0) 
            {
                lock (_lockObj) 
                {
                    _db.InsertAll(ListDb, "OR Replace", true); 
                }
            }

            return (true, string.Empty, numDb);
        }


        /// <summary>
        /// 导入出港账单数据
        /// </summary>
        /// <param name="filePath">文件路径</param>
        /// <param name="dateFull">完整日期字符串</param>
        /// <returns>导入结果、消息和导入记录数量</returns>
        private (bool res, string mark, int ImpNum) ImportOutboundData(string filePath, string dateFull)
        {
            // 确保数据表存在
            _db.CreateTable<OutboundInvoiceData>();
            // 读取文件内容
            var rows = MiniExcel.Query(filePath, useHeaderRow: true);
            
            // 用于存储待插入的数据
            var ListDb = new List<OutboundInvoiceData>();
            int numDb = 0;
            
            // 遍历文件中的每一行数据
            foreach (IDictionary<string, object> row in rows.Select(v => (IDictionary<string, object>)v))
            {
                // 创建数据实体并填充字段
                var item = new OutboundInvoiceData
                {
                    Date = dateFull,
                    单号 = (string)row["运单号"],
                    结算对象 = string.Empty,  // 初始化时为空，后续可能通过匹配填充
                    分部 = string.Empty,  // 初始化时为空，后续可能通过匹配填充
                    电子面单账号名称 = (string)row["电子面单账号名称"],
                    结算重量_kg = (Double)row["结算重量(kg)"],
                    派费合计 = (Double)row["派费合计"],
                    中转费合计 = (Double)row["中转费合计"],
                    ImportTime = DateTime.Now,
                    IsMatched = 0  // 标记为未匹配状态
                };
                
                // 添加有效数据到列表
                if (item.单号 != null) 
                {
                    ListDb.Add(item); 
                    numDb++;
                }
                
                // 批量插入数据
                if (ListDb.Count >= _batchSize)
                {
                    lock (_lockObj) 
                    {
                        _db.InsertAll(ListDb, "OR Replace", true); 
                    }
                    ListDb.Clear();
                }
            }

            // 插入剩余的数据
            if (ListDb.Count > 0) 
            {
                lock (_lockObj) 
                {
                    _db.InsertAll(ListDb, "OR Replace", true); 
                }
            }

            return (true, string.Empty, numDb);
        }


        /// <summary>
        /// 导入中心中转费数据
        /// </summary>
        /// <param name="filePath">文件路径</param>
        /// <param name="dateFull">完整日期字符串</param>
        /// <returns>导入结果、消息和导入记录数量</returns>
        private (bool res, string mark, int ImpNum) ImportCenterTransferData(string filePath, string dateFull)
        {
            // 确保数据表存在
            _db.CreateTable<CenterTransferFeeData>();
            // 读取文件内容
            var rows = MiniExcel.Query(filePath, useHeaderRow: true);
            
            // 用于存储待插入的数据
            var ListDb = new List<CenterTransferFeeData>();
            int numDb = 0;
            
            // 遍历文件中的每一行数据
            foreach (var row in rows)
            {
                // 创建数据实体并填充字段
                var item = new CenterTransferFeeData
                {
                    Date = dateFull,
                    单号 = row.运单编号,
                    结算对象 = string.Empty,  // 初始化时为空，后续可能通过匹配填充
                    分部 = string.Empty,  // 初始化时为空，后续可能通过匹配填充
                    客户名称 = row.客户名称,
                    结算重量 = row.结算重量,
                    中转费合计 = row.中转费合计,
                    ImportTime = DateTime.Now,
                    IsMatched = 0  // 标记为未匹配状态
                };
                
                // 添加有效数据到列表
                if (item.单号 != null) 
                {
                    ListDb.Add(item); 
                    numDb++;
                }
                
                // 批量插入数据
                if (ListDb.Count >= _batchSize)
                {
                    lock (_lockObj) 
                    {
                        _db.InsertAll(ListDb, "OR Replace", true); 
                    }
                    ListDb.Clear();
                }
            }

            // 插入剩余的数据
            if (ListDb.Count > 0) 
            {
                lock (_lockObj) 
                {
                    _db.InsertAll(ListDb, "OR Replace", true); 
                }
            }

            return (true, string.Empty, numDb);
        }
    }
}
